﻿using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using xres;

namespace xuni
{

    /// <summary>
    /// 主线程(渲染线程)定时器. 注意: 不要在异步线程上使用该类
    /// </summary>
    public class Scheduler
    {

        static Pool<Scheduler> s_pool;
        static ListValueMap<UITag, Scheduler> s_runningTagSchedulesDict; //正在运行的带tag的Scheduler

        public static Scheduler Obtain()
        {
            if (null == s_pool)
                s_pool = new Pool<Scheduler>((sender) => new Scheduler(), (sender, msg) => { msg.Reset(); });

            var ret = s_pool.Obtain();
            return ret;
        }

        public static Scheduler Obtain(float delay, Action<object> selector)
        {
            var timer = Obtain();

            timer.delay = delay;
            timer.selector = selector;

            return timer;
        }

        public static Scheduler Obtain(float interval, int loop, Action<object> selector)
        {
            var timer = Obtain();

            timer.delay = 0;
            timer.interval = interval;
            timer.loop = loop;
            timer.selector = selector;

            return timer;
        }

        public static Scheduler Obtain(float delay, float interval, int loop, Action<object> selector)
        {
            var timer = Obtain();

            timer.delay = delay;
            timer.interval = interval;
            timer.loop = loop;
            timer.selector = selector;

            return timer;
        }

        public static void StopAndRecycle(Scheduler s)
        {
            if (null == s)
                return;

            s.Stop();
            Recycle(s);
        }

        public static void Recycle(Scheduler s)
        {
            if (null == s_pool)
                return;

            s_pool.Recycle(s);
        }


        //==========

        /*
        public static void StopById(int id)
        public static void StopAllByTag(string tag)
        */


        public static void StopAllByTag(UITag tag)
        {
            if (null == s_runningTagSchedulesDict)
                return;

            if (s_runningTagSchedulesDict.TryGetList(tag, out var list))
            {
                for (var i = 0; i < list.Count; ++i)
                {
                    list[i].Stop();
                }
                list.Clear();
            }
            UITag.Recycle(tag);
        }
        

        //==========



        public Action<Scheduler> selector;

        UITag _tag = null; //运行时设置tag没有任何效果

        /// <summary>
        /// 执行延迟
        /// </summary>
        public float delay;

        /// <summary>
        /// 执行间隔(允许运行中修改)
        /// </summary>
        public float interval;

        /// <summary>
        /// 执行次数
        /// </summary>
        public int loop;

        public bool timeScale = true;

        /// <summary>
        /// 执行结束时自动回收
        /// </summary>
        bool _autoRecycle;

        UserData _userData;
        public UserData UserData
        {
            get
            {
                if (null == _userData)
                    _userData = new UserData();

                return _userData;
            }
        }

        float _leftTime;
        float _leftLoop;
        bool _running;
        public bool IsRunning { get { return _running; } }

        protected Scheduler()
        {
            _OnUpdate = OnUpdate;
        }

        void Reset()
        {
            _tag = null;
            selector = null;
            delay = 0;
            interval = 0;
            loop = 0;
            timeScale = true;
            _autoRecycle = false;
            if (null != _userData)
            {
                _userData.i = 0;
                _userData.s = "";
                _userData.obj = null;
            }

            _leftTime = 0;
            _leftLoop = 0;
            _running = false;
        }

        public Scheduler SetAutoRecycle(bool b)
        {
            if (_running)
                Debug.Log($"Scheduler: set ignore when running");
            else
                _autoRecycle = b;

            return this;
        }

        public void SetTag(UITag tag)
        {
            if (_running)
            {
                Debug.Log($"Scheduler: running, tag set ignore");
                return;
            }
            _tag = tag;
        }

        public bool Start()
        {
            if (_running)
                return false;

            _leftTime = delay + interval;
            _leftLoop = loop;

            _Start();
            return true;
        }

        public void Restart()
        {
            _leftTime = delay + interval;
            _leftLoop = loop;

            if (_running)
                return;

            _Start();
        }

        void _Start()
        {
            if (s_pool.Contains(this))
            {
                Debug.LogError($"Scheduler has recycle");
                return;
            }

            //开始运行的时候(Start, Resume), 如果有tag, 则加入dict; 停止的时候移除
            if (null != _tag)
            {
                if (null == s_runningTagSchedulesDict)
                    s_runningTagSchedulesDict = new ListValueMap<UITag, Scheduler>();

                s_runningTagSchedulesDict.AddOne(_tag, this);
            }

            _running = true;
            xBehaviour.OnUpdate += _OnUpdate;
        }

        public bool Stop()
        {
            if (!_running)
                return false;

            _running = false;
            xBehaviour.OnUpdate -= _OnUpdate;

            if (null != _tag)
            {
                s_runningTagSchedulesDict.RemoveOne(_tag, this);
            }

            return true;
        }

        public void Resume()
        {
            if (_running)
                return;

            if (_leftTime <= 0 && 0 == _leftLoop) //已执行完没必要恢复了
                return;

            _Start();
        }

        Action<object> _OnUpdate;
        void OnUpdate(object sender)
        {
            if (!_running)
            {
                xBehaviour.OnUpdate -= _OnUpdate;
                return;
            }

            var dt = timeScale ? Time.deltaTime : Time.unscaledDeltaTime;
            _leftTime -= dt;

            var leftTime = _leftTime;
            if (leftTime > 0)
                return;

            var isEnd = false;

            if (_leftLoop > 0)
                _leftLoop -= 1;

            if (0 == _leftLoop)
            {
                Stop();
                isEnd = true;
            }
            else if (_leftLoop < 0) //无限循环
            {
                //do nothing
            }

            SafeInvokeDelegate();
            if (isEnd)
            {
                if (_running) //在selector中调用了Start的情况
                {
                }
            }
            else
            {
                if (!_running) //在selector中调用了Stop的情况
                {
                    isEnd = true;
                }
            }

            if (isEnd)
            {
                _leftTime = 0;
                if (_autoRecycle)
                {
                    Debug.Log($"Scheduler end: autoRecycle");
                    Scheduler.Recycle(this);
                }
            }
            else
            {
                if (false) Debug.Log($"Scheduler: next loop: {_leftTime}, {interval}");
                _leftTime += interval; //下一个loop(在selector中修改执行间隔也没有影响)
                if (_leftTime < 0)
                    _leftTime = 0;
            }
        }

        void SafeInvokeDelegate()
        {
            if (null == selector)
                return;

            try
            {
                selector.Invoke(this);
            }
            catch (Exception ex)
            {
                Debug.LogError(ex.Message);
                Debug.LogError(ex.StackTrace);
            }
        }


    } //end of class


}
